package com.example.batch.controller;


import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.CRC32;

public class FileMap {
    public static void main(String[] args) {
        MemoryMapTest.test();
    }
}

/*
    2.6 内存映射文件

    2.6.1 内存映射文件的性能

    java.nio包使用内存映射的过程：
        1.得到一个通道
            FileChannel channel = FileChannel.open(path,options);
        2.通过map方法从这个通道中获得一个ByteBuffer，可以指定要映射的文件区域与模式
            FileChannel.MapMode.READ_ONLY:
            FileChannel.MapMode.READ_WRITE:
            FileChannel.MapMode.PRIVATE:
        3.通过ByteBuffer读写数据

            //顺序遍历缓冲区所有字节
            while(buffer.hasRemaining()){
                byte b = buffer.get();
                ...
            }

            //随机访问缓冲区字节
            for(int i =0; i<buffer.limit(); i++)
                byte b = buffer.get(i);
                ...
            }

            //读写字节数组
            get(byte[] bytes)
            get(byte[] bytes, int offset, int len)

            getInt,getLong,getShort,getChar,getFloat,getDouble,order

            buffer.order(ByteOrder.LITTLE_ENDIAN);


 */
class MemoryMapTest {
    /*
        待会找到Java编程思想中关于CRC32的用法，对比一下

            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            ZipOutputStream zos = new ZipOutputStream(csum);
     */
    public static long checksumInputStream(Path filename) {
        try (InputStream in = Files.newInputStream(filename)) {
            CRC32 crc = new CRC32();
            int c;
            while ((c = in.read()) != -1) {
                crc.update(c);
            }
            return crc.getValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    public static long checksumBufferedInputStream(Path filename) {
        try (InputStream in = new BufferedInputStream(Files.newInputStream(filename))) {
            CRC32 crc = new CRC32();
            int c;
            while ((c = in.read()) != -1) {
                crc.update(c);
            }
            return crc.getValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    public static long checksumRandomAccseeFile(Path filename) {
        try (RandomAccessFile file = new RandomAccessFile(filename.toFile(), "r")) {

            long length = file.length();
            CRC32 crc = new CRC32();

            for (long p = 0; p < length; p++) {
                file.seek(p);
                int c = file.readByte();
                crc.update(c);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    public static long checksumMappedFile(Path filename) {
        try (FileChannel channel = FileChannel.open(filename)) {

            CRC32 crc = new CRC32();
            int length = (int) channel.size();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);

            for (int p = 0; p < length; p++) {
                int c = buffer.get(p);
                crc.update(c);
            }
            return crc.getValue();

        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    public static void test() {
        Path filename = Paths.get("D://accounts.json");

        //InputStream
        System.out.println("InputStream:");
        long start = System.currentTimeMillis();
        long crcValue = checksumInputStream(filename);
        long end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");

        //RandomAccessFile
        System.out.println("RandomAccessFile:");
        start = System.currentTimeMillis();
        crcValue = checksumRandomAccseeFile(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");

        //BufferedInputStream
        System.out.println("BufferedInputStream:");
        start = System.currentTimeMillis();
        crcValue = checksumBufferedInputStream(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");

        //Mapped
        System.out.println("InputStream:");
        start = System.currentTimeMillis();
        crcValue = checksumMappedFile(filename);
        end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
    }
}

/*
    2.6.2 缓冲区数据结构

    每个缓冲区都具有：
        1.一个容量，它永远不能被改变
        2.一个读写位置，写一个值将在此读写
        3.一个界限，超过它进行读写是没有意义的
        4.一个可选的标记，用于重复一个读入或写出操作

    0<=标记<=位置<=界限<=容量
 */

/*
    2.6.3 文件加锁机制

        FileLock lock = channel.lock();
        FileLock lock = channel.tryLock();

        FileLock lock(long start,long size,boolean shared)
        FileLock tryLock(long start, long size,boolean shared)

        如果shared的标志为false，则锁定文件的目的是读写，如果为true，则这是一个共享锁，
        它允许多个进程从文件中读入，并阻止任何进程获得独占的锁。调用FileLock类的isShared
        方法可以查询你持有的锁的类型。
 */
